package com.fadis.data.zset;

import com.fadis.cmd.ZSetScoreRange;
import com.fadis.data.base.StringZSet;
import com.fadis.utils.FadisUtils;
import com.fadis.cmd.ExistOption;

import java.util.ArrayList;
import java.util.List;
import java.util.TreeMap;

public class ZSet<T extends Comparable<T>> {

    private ZSkipList<T> list;
    protected TreeMap<T, Double> dict;

    public ZSet() {
        list = new ZSkipList<T>();
        dict = new TreeMap<>();
    }

    // 返回-1忽略， 0新增，1更新
    // 忽略的数量（socre不变），更新的数量（score变了）
    public int add(double score, T obj){
        return add(score, obj, ExistOption.IGNORE, false);
    }

    public int add(double score, T obj, ExistOption existOption, boolean updateByIncr){
        int r = 0;
        double newScore = score;
        if(dict.containsKey(obj)){
            if(existOption == ExistOption.NX){
                return -1; // 忽略
            }else{
                double oldScore = dict.get(obj);
                if(updateByIncr){
                    newScore += oldScore;
                    list.delete(oldScore, obj);
                    r = 1;
                }else{
                    if(oldScore == score) {
                        return -1;
                    } else{
                        list.delete(oldScore, obj);
                        r = 1;
                    }
                }
            }

        }

        list.insert(newScore, obj);
        dict.put(obj, score);
        return r;
    }

    public Double score(T obj){
        return dict.get(obj);
    }

    public Double incrby(T obj, double increment){
        if(!dict.containsKey(obj)){
            add(0, obj);
        }

        double score = dict.get(obj);
        list.delete(score, obj);

        score += increment;
        list.insert(score, obj);
        dict.put(obj, score);
        return score;
    }

    public long card(){
        return dict.size();
    }

    public long count(double min, boolean minInclude, double max, boolean maxInclude){
        ZSkipListNode<T> nodeStart = list.firstInRange(min, max);
        ZSkipListNode<T> nodeEnd = list.lastInRange(min, max);

        if(nodeStart == null || nodeEnd == null) return 0;
        if(nodeStart.score == min && !minInclude){
            nodeStart = nodeStart.level[0].forward;
        }
        if(nodeEnd.score == max && !maxInclude){
            nodeEnd = nodeEnd.backward;
        }
        if(nodeStart == null || nodeEnd == null) return 0;
        if(nodeStart == nodeEnd) return 1;
        return list.getRank(nodeEnd.score, nodeEnd.obj) - list.getRank(nodeStart.score, nodeStart.obj) + 1;
    }

    public List<ZSkipListNode<T>> range(int start, int stop){
        int startReal = FadisUtils.getNormalIndex(start, dict.size());
        int stopReal = FadisUtils.getNormalIndex(stop, dict.size());
        if(stopReal >= dict.size()) stopReal = dict.size() - 1;
        if(startReal > stopReal) return null;
        return list.range(startReal, stopReal);
    }

    public List<ZSkipListNode<T>> revrange(int start, int stop){
        int startReal = FadisUtils.getNormalIndex(start, dict.size());
        int stopReal = FadisUtils.getNormalIndex(stop, dict.size());
        if(stopReal >= dict.size()) stopReal = dict.size() - 1;
        if(startReal > stopReal) return null;

        int start2 = dict.size() - stop - 1;
        int stop2 = dict.size() - startReal - 1;

        List<ZSkipListNode<T>> list2 = list.range(start2, stop2);
        if(list2 == null) return null;
        List<ZSkipListNode<T>> listRev = new ArrayList<>();
        if(list2.size() == 0) return listRev;


        for (int i = list2.size() - 1; i >= 0; i--) {
            listRev.add(list2.get(i));
        }

        return listRev;
    }

    public List<ZSkipListNode<T>> rangeByScore(double min, boolean minInclude, double max, boolean maxInclude){
        List<ZSkipListNode<T>> r = list.rangeByScore(min, minInclude, max, maxInclude);
        return r;
    }

    public List<ZSkipListNode<T>> revRangeByScore(double min, boolean minInclude, double max, boolean maxInclude){
        return list.revRangeByScore(min, minInclude, max, maxInclude);
    }

    public long rank(T obj){
        if(!dict.containsKey(obj)) return -1;
        return list.getRank(dict.get(obj), obj);
    }

    public long revRank(T obj){
        if(!dict.containsKey(obj)) return -1;
        long rank = list.getRank(dict.get(obj), obj);
        return dict.size() - rank - 1;
    }

    public int rem(T obj){
        if(!dict.containsKey(obj)) return -1;
        int r = list.delete(dict.get(obj), obj);
        dict.remove(obj);
        return r;
    }

    public long remRangeByRank(long start, long stop){
        long startReal = FadisUtils.getNormalIndex(start, dict.size());
        long stopReal = FadisUtils.getNormalIndex(stop, dict.size());
        if(stopReal >= dict.size()) stopReal = dict.size() - 1;
        if(startReal > stopReal) return 0;

        return list.deleteRangeByRank(start, stop, dict);
    }

    public long remRangeByScore(double min, boolean minInclude, double max, boolean maxInclude){
        return list.deleteRangeByScore(min, minInclude, max, maxInclude, dict);
    }

    public static StringZSet inter(List<StringZSet> sets, List<Double> weights, String aggregate){
        if(weights == null || weights.size() == 0){
            weights = new ArrayList<>();
            for (int i = 0; i < sets.size(); i++) {
                weights.add(1.0);
            }
        }

        if(aggregate == null) aggregate = "sum";
        // 先找到sets中最小的集合，看这个集合里每个元素是否存在于其他集合
        // 默认情况下，结果集中某个成员的 score 值是所有给定集下该成员 score 值之和--sum, 也可以是min，max
        StringZSet r = new StringZSet();
        StringZSet min = sets.get(0);
        for (int i = 1; i < sets.size(); i++) {
            if(sets.get(i) == null) continue;
            if(min == null || sets.get(i).card() < min.card()){
                min = sets.get(i);
            }
        }

        for (String obj: min.dict.keySet()){
            boolean contains = true;
            double newScore = min.dict.get(obj);
            for (int i = 0; i < sets.size(); i++) {
                if(min == null || min == sets.get(i)) continue;
                if(sets.get(i).dict.containsKey(obj)) {
                    double score = sets.get(i).dict.get(obj) * weights.get(i);
                    if(aggregate.equals("sum")){
                        newScore += score;
                    }else if(aggregate.equals("min")){
                        if(score < newScore) newScore = score;
                    }else if(aggregate.equals("max")){
                        if(score > newScore) newScore = score;
                    }
                }else{
                    contains = false;
                    break;
                }
            }
            if(contains){
                r.add(newScore, obj);
            }
        }

        return r;
    }

    public static StringZSet union(List<StringZSet> sets, List<Double> weights, String aggregate){
        // 默认情况下，结果集中某个成员的 score 值是所有给定集下该成员 score 值之和
        if(weights == null || weights.size() == 0){
            weights = new ArrayList<>();
            for (int i = 0; i < sets.size(); i++) {
                weights.add(1.0);
            }
        }

        if(aggregate == null) aggregate = "sum";

        StringZSet r = new StringZSet();
        // 并集，就是把所有set遍历一下
        // -- 已经出现的，查看下score是否需要替换
        // -- 未出现的，add进去
        for (int i = 0; i < sets.size(); i++) {
            for (String obj: sets.get(i).dict.keySet()){
                double oldScore = r.dict.containsKey(obj) ? r.dict.get(obj) : 0;
                double newScore2 = sets.get(i).dict.get(obj) * weights.get(i);

                if(aggregate.equals("sum")){
                    oldScore += newScore2;
                }else if(aggregate.equals("min")){
                    if(newScore2 < oldScore) oldScore = newScore2;
                }else if(aggregate.equals("max")){
                    if(newScore2 > oldScore) oldScore = newScore2;
                }

                r.add(oldScore, obj);
            }
        }

        return r;
    }

    public long deleteRangeByLex(T min, boolean minInclude, T max, boolean maxInclude){
        return list.deleteRangeByLex(min, minInclude, max, maxInclude, dict);
    }

    public long lexCount(T min, boolean minInclude, T max, boolean maxInclude){
        return list.lexCount(min, minInclude, max, maxInclude);
    }

    public List<ZSkipListNode<T>> rangeByLex(T min, boolean minInclude, T max, boolean maxInclude){
        return list.rangeByLex(min, minInclude, max, maxInclude);
    }

    public static void main(String[] args) {
        ZSet s = new ZSet();
        s.add(12, "b");
        s.add(11, "a");
        s.add(14, "d");
        s.add(13, "c");
        s.add(16, "f");
        s.add(15, "e");
        s.list.printAllLevels();

        s.incrby("e", -30);
        s.list.printAllLevels();

        ZSetScoreRange range = ZSetScoreRange.from("-100", "15");
        System.out.println(range);
        System.out.println(s.count(range.min, range.minInclude, range.max, range.maxInclude));

        List<ZSkipListNode> list = s.rangeByScore(-100, true, 15, true);
        System.out.println(list);
    }
}
